package utility.files;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;

public class FileUtility {
	private Lines _report;

	public FileUtility(){
		this(new Lines());
	}
	
	public String asHex(File file){		
		byte[] bytes = byteArrayFrom(file);
		final byte[] hexhars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
				'9', 'a', 'b', 'c', 'd', 'e', 'f' };

		StringBuilder result = new StringBuilder(2 * bytes.length);

		for (int i = 0; i < bytes.length; i++) {
			int v = bytes[i] & 0xff;
			result.append((char) hexhars[v >> 4]);
			result.append((char) hexhars[v & 0xf]);
		}
		return result.toString();
	}
	
	public byte[] byteArrayFrom(File file){
	    
        long length = file.length();
        byte[] bytes = new byte[(int)length];
    
        if (length > Integer.MAX_VALUE) {
            _report.putLine("File "+file.getName()+" is too Large!");
            return bytes;
        }
        
        InputStream is = null;
    
        try {
        	is = new FileInputStream(file);
        	int offset = 0;
        	int numRead = 0;
			while (offset < bytes.length
			       && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {
			    offset += numRead;
			}
			if (offset < bytes.length) {
				_report.putLine("Could not completely read file "+file.getName());
			}
		} catch (IOException e) {
		} finally{
			close(is);
		}
        return bytes;
	}
	
	public FileUtility(Lines report) {
		_report = report;
	}

	public void close(InputStream inputStream) {
		if(inputStream!=null){
			try {
				inputStream.close();
			} catch (IOException e) {
				_report.putLine("Failed to close InputStream!");
			}
		}
	}
	
	public void close(InputStreamReader inputStream) {
		if(inputStream!=null){
			try {
				inputStream.close();
			} catch (IOException e) {
				_report.putLine("Failed to close InputStreamReader!");
			}
		}
	}
	
	public void close(OutputStream outputStream) {
		if (outputStream!=null)
			try {
				outputStream.close();
			} catch (IOException e) {
				_report.putLine("Failed to close OutputStream!");
			}
	}
	
	public void copyFromTo(String source, String destination){
		InputStream inputStream = inputStreamFor(source);
		OutputStream outputStream = outputStreamFor(destination);
		if (inputStream==null||outputStream==null) return;
		try {
			copyFromTo(inputStream, outputStream);
		} catch (IOException e) {
			_report.putLine("Errore nel copiare il file da "+source+" a "+destination);
		}
	}
	
	public void copyFromTo(File source, File destination){
		InputStream inputStream = inputStreamFor(source);
		OutputStream outputStream = outputStreamFor(destination);
		if (inputStream==null||outputStream==null) return;
		try {
			copyFromTo(inputStream, outputStream);
		} catch (IOException e) {
			_report.putLine("Errore nel copiare il file da "+source+" a "+destination);
		}
	}

	private void copyFromTo(InputStream inputStream, OutputStream outputStream) throws IOException {
		BufferedInputStream is = new BufferedInputStream(inputStream);
		BufferedOutputStream os = new BufferedOutputStream(outputStream);
		try{
			int c;
			while ((c=is.read())!=-1) os.write(c);
		} catch(IOException e) {
			throw e;
		}
		finally {
			close(is);
			close(os);
		}
	}
	
	public InputStream inputStreamFor(String fileName) {
		try {
			if (!fileName.startsWith("http:")) return new FileInputStream(fileName);
			
			URL url = new URL(fileName);
			URLConnection connection = url.openConnection();
			connection.setConnectTimeout(5000);
			InputStream input = connection.getInputStream();
			return input;
		} catch (FileNotFoundException e) {
			_report.putLine("File "+fileName+" NOT FOUND");
			return null;
		} catch (MalformedURLException e) {
			_report.putLine("URL malformed");
			return null;
		} catch (IOException e) {
			_report.putLine("IOException");
			return null;
		}
			
	}
	
	public OutputStream outputStreamFor(String fileName){
		return outputStreamFor(new File(fileName));
	}

	public Lines report() {
		return _report;
	}

	public void createNew(File file) {
		file.delete();
		try {
			file.createNewFile();
			_report.clean();
		} catch (IOException e) {
			try {
				_report.putLine("Failed to create file: "+file.getCanonicalPath());
			} catch (IOException e1) {
				_report.putLine("Failed to create file: "+file.getName());
			}
		}
	}
	
	public void writeUsing(byte[] bytes, OutputStream outputStream){
		try{			
			outputStream.write(bytes);
		} catch (IOException e) {
			_report.putLine("IO Exception writing On File");
		}
		finally {
			close(outputStream);
		}
	}

	public InputStream inputStreamFor(File aFile) {
		try {
			InputStream inputStream = new FileInputStream(aFile);
			return inputStream;
		} catch (FileNotFoundException e) {
			_report.putLine("File "+aFile.getName()+" NOT FOUND!");
			return null;
		}
	}

	public OutputStream outputStreamFor(File aFile) {
		try {
			FileOutputStream output = new FileOutputStream(aFile);
			return output;
		} catch (FileNotFoundException e) {
			_report.putLine(aFile.getName()+" Not Found!");
			return null;
		}
	}

	public static boolean isContentEqualTo(File file1, File file2) {
		byte[] bytes1 = new FileUtility().byteArrayFrom(file1);
		byte[] bytes2 = new FileUtility().byteArrayFrom(file2);
		return Arrays.equals(bytes1, bytes2);
	}

}
